#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QWidget>
#include <QToolBar>
#include <QDebug>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QKeyEvent>
#include <QFileDialog>
#include <QMessageBox>
#include <QApplication>
#include <QStyle>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    initGame();
    initUi();
    initConnections();
}

void MainWindow::initGame()
{
    game = new Game(40, 40);
    gameView = new GameView(game);
}

void MainWindow::initUi()
{
    // initial size
    resize(sizeHint());

    // window settings
    // setWindowTitle("Snake");
    setWindowIcon(QIcon(":/icon/snake.ico"));

    // actions
    actLoad = new QAction("Load", this);
    actSave = new QAction("Save", this);
    actStart = new QAction("Start", this);
    actPause = new QAction("Pause", this);
    actRestart = new QAction("Restart", this);
    actResume = new QAction("Resume", this);
    actQuit = new QAction("Quit", this);
    actAbout = new QAction("About", this);

    // set icon for actions
    QStyle *style = QApplication::style();
    actLoad->setIcon(style->standardIcon(QStyle::SP_DialogOpenButton));
    actSave->setIcon(style->standardIcon(QStyle::SP_DialogSaveButton));
    actStart->setIcon(style->standardIcon(QStyle::SP_ArrowRight));
    actPause->setIcon(style->standardIcon(QStyle::SP_MediaPause));
    actRestart->setIcon(style->standardIcon(QStyle::SP_BrowserReload));
    actResume->setIcon(style->standardIcon(QStyle::SP_MediaPlay));
    actQuit->setIcon(style->standardIcon(QStyle::SP_TitleBarCloseButton));
    actAbout->setIcon(style->standardIcon(QStyle::SP_MessageBoxInformation));

    // menu bar
    QMenu *menuFile = new QMenu("&File", this);
    menuFile->addAction(actLoad);
    menuFile->addAction(actSave);

    QMenu *menuGame = new QMenu("&Game", this);
    menuGame->addAction(actStart);
    menuGame->addAction(actRestart);
    menuGame->addSeparator();
    menuGame->addAction(actPause);
    menuGame->addAction(actResume);
    menuGame->addSeparator();
    menuGame->addAction(actQuit);

    menuBar()->addMenu(menuFile);
    menuBar()->addMenu(menuGame);
    // menuBar()->setStyleSheet("background-color: rgba(128, 128, 128, 0.4);");

    // tool bar
    QToolBar *toolBar = new QToolBar(this);
    toolBar->addAction(actLoad);
    toolBar->addAction(actSave);
    toolBar->addAction(actStart);
    toolBar->addAction(actRestart);
    toolBar->addAction(actPause);
    toolBar->addAction(actResume);
    toolBar->addAction(actQuit);
    toolBar->addAction(actAbout);
    // toolBar->setStyleSheet("background-color: rgba(0, 128, 128, 0.4);");
    addToolBar(toolBar);

    // buttons
    btnLoad = new QPushButton;
    btnSave = new QPushButton;
    btnQuit = new QPushButton;
    btnLoad->setText("Load");
    btnSave->setText("Save");
    btnQuit->setText("Quit");

    btnStartOrRestart = new QPushButton;
    btnPauseResume = new QPushButton;

    QHBoxLayout *buttons1 = new QHBoxLayout;
    buttons1->addWidget(btnLoad);
    buttons1->addWidget(btnSave);
    buttons1->addWidget(btnQuit);

    QHBoxLayout *buttons2 = new QHBoxLayout;
    buttons2->addWidget(btnStartOrRestart);
    buttons2->addWidget(btnPauseResume);

    // statistics
    lblTime = new QLabel;
    lblTime->setMaximumHeight(30);
    lblTime->setAlignment(Qt::AlignLeft);
    lblMovedDistance = new QLabel;
    lblMovedDistance->setMaximumHeight(30);
    lblMovedDistance->setAlignment(Qt::AlignHCenter);
    lblScore = new QLabel;
    lblScore->setMaximumHeight(30);
    lblScore->setAlignment(Qt::AlignRight);

    QHBoxLayout *statistics = new QHBoxLayout;
    statistics->addWidget(lblTime);
    statistics->addWidget(lblMovedDistance);
    statistics->addWidget(lblScore);

    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addLayout(statistics);
    mainLayout->addWidget(gameView);
    mainLayout->addLayout(buttons2);
    mainLayout->addLayout(buttons1);

    // central widget
    QWidget *widget = new QWidget;
    widget->setLayout(mainLayout);
    this->setCentralWidget(widget);

    setupInitialWidgets();
    setSharedButtons();
}

/**
 * @brief MainWindow::setupInitialWidgets
 * @note shared buttons are not processed, call setSharedButtons() when initializing
 */
void MainWindow::setupInitialWidgets()
{
    actLoad->setEnabled(true);
    actSave->setEnabled(false);
    actStart->setEnabled(true);
    actPause->setEnabled(false);
    actRestart->setEnabled(false);
    actResume->setEnabled(false);

    btnLoad->setEnabled(true);
    btnSave->setEnabled(false);

    lblScore->setText("score: 0");
    lblMovedDistance->setText("moved distance: 0");
    lblTime->setText("time: 0s");
}

void MainWindow::initConnections()
{
    // actions
    connect(actLoad, &QAction::triggered, this, &MainWindow::loadGame);
    connect(actSave, &QAction::triggered, this, &MainWindow::saveGame);
    connect(actStart, &QAction::triggered, game, &Game::ready);
    connect(actPause, &QAction::triggered, game, &Game::pause);
    connect(actRestart, &QAction::triggered, game, &Game::restart);
    connect(actResume, &QAction::triggered, game, &Game::start);
    connect(actQuit, &QAction::triggered, this, &MainWindow::close);
    connect(actAbout, &QAction::triggered, this, &MainWindow::showAbout);

    // buttons
    connect(btnLoad, &QPushButton::clicked, this, &MainWindow::loadGame);
    connect(btnSave, &QPushButton::clicked, this, &MainWindow::saveGame);
    connect(btnQuit, &QPushButton::clicked, this, &MainWindow::close);
    connect(btnStartOrRestart, &QPushButton::clicked, [this]() {
        if (game->getState() == Game::State::initial)
            game->ready();
        else
            game->restart();
    });
    connect(btnPauseResume, &QPushButton::clicked, [this]() {
        if (game->getState() == Game::State::paused)
            game->start();
        else
            game->pause();
    });

    // game logic
    connect(game, SIGNAL(gameUpdate()), gameView, SLOT(onGameUpdate()));
    connect(game, SIGNAL(gameStateChanged(Game::State, Game::State)),
            gameView, SLOT(onGameStateChanged(Game::State, Game::State)));
    connect(game, SIGNAL(gameUpdate()), this, SLOT(onGameUpdate()));
    connect(game, &Game::gameStateChanged, this, &MainWindow::onGameStateChanged);
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);

    qDebug() << "MainWindow: " << size();
}

void MainWindow::setSharedButtons()
{
    auto state = game->getState();
    // button texts
    if (state == Game::State::initial)
        btnStartOrRestart->setText("Start");
    else
        btnStartOrRestart->setText("Restart");
    if (state == Game::State::paused)
        btnPauseResume->setText("Resume");
    else
        btnPauseResume->setText("Pause");
    // button state (enabled)
    btnPauseResume->setEnabled(
        state != Game::State::initial && state != Game::State::over
    );
    btnStartOrRestart->setEnabled(
        state != Game::State::ready && state != Game::State::started
    );
}

void MainWindow::onGameStateChanged(Game::State prev, Game::State next)
{
    Q_UNUSED(prev);
    // Q_UNUSED(next);

    switch (next) {
    case Game::State::initial:
        setupInitialWidgets();
        break;
    case Game::State::ready:
    case Game::State::started:
        actLoad->setEnabled(false);
        actSave->setEnabled(false);
        actStart->setEnabled(false);
        actPause->setEnabled(true);
        actRestart->setEnabled(false);
        actResume->setEnabled(false);

        btnLoad->setEnabled(false);
        btnSave->setEnabled(false);

        break;
    case Game::State::paused:
        actLoad->setEnabled(false);
        actSave->setEnabled(true);
        actStart->setEnabled(false);
        actPause->setEnabled(false);
        actRestart->setEnabled(true);
        actResume->setEnabled(true);

        btnLoad->setEnabled(false);
        btnSave->setEnabled(true);

        break;
    case Game::State::over:
        actLoad->setEnabled(false);
        actSave->setEnabled(false);
        actStart->setEnabled(false);
        actPause->setEnabled(false);
        actRestart->setEnabled(true);
        actResume->setEnabled(false);

        btnLoad->setEnabled(false);
        btnSave->setEnabled(false);

        break;
    default:
        break;
    }

    setSharedButtons();
}

void MainWindow::onGameUpdate()
{
    QString scoreText = "score: " + QString::number(game->getScore());
    QString movedDisText = "moved distance: " + QString::number(game->getMovedDistance());
    QString timeText = "time: " + QString::number(game->getGameTime()) + "s";

    lblScore->setText(scoreText);
    lblMovedDistance->setText(movedDisText);
    lblTime->setText(timeText);
}

void MainWindow::saveGame()
{
    QString fileName = QFileDialog::getSaveFileName(this, "Save File (Game State)");
    if (fileName.isEmpty())
        fileName = "snake.json";

    if (game->save(fileName))
        QMessageBox::information(nullptr, "ok", "successfully saved file " + fileName);
    else
        QMessageBox::warning(nullptr, "error", "failed to save " + fileName);
}

void MainWindow::loadGame()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open File (Game State)");

    if (game->load(fileName))
        QMessageBox::information(nullptr, "ok", "successfully loaded file " + fileName);
    else
        QMessageBox::warning(nullptr, "error", "failed to load " + fileName);
}

void MainWindow::showAbout()
{
    QString info = "by hzx (2019011256)\npress SPACE to start/pause/resume the game";
    QMessageBox::information(nullptr, "about", info);
}

MainWindow::~MainWindow()
{
    delete ui;
    delete gameView;
    delete game;
}

